package gateway.filter;

import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.stereotype.Component;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import gateway.utils.JwtUtil;
import java.util.Map;
import java.util.HashMap;
import java.util.Arrays;
import java.util.List;

@Component
public class AdminFilter implements GlobalFilter, Ordered {
    private static final Logger log = LoggerFactory.getLogger(AdminFilter.class);
    
    // 后台路径前缀列表
    private static final List<String> ADMIN_PATH_PREFIXES = Arrays.asList(
        "/jiang",       // 主后台路径
        "/order/jiang", // 订单后台路径
        "/goods/jiang", // 商品后台路径
        "/shop/jiang",  // 商铺后台路径
        "/auth/jiang",   // 认证后台路径
        "/files/jiang",   // 文件后台路径
        "/comment/jiang",  // 评论后台路径
        "/cart/jiang/",  // 购物车后台路径
        "/chat/admin",
        "/chat/close",
        "/chat/getUserChatHistory"
    );
    
    // 不需要验证的后台路径
    private static final List<String> NO_AUTH_PATHS = Arrays.asList(
        "/jiang/login",
        "/merchant/login"
    );
    
    @Autowired
    private JwtUtil jwtUtil;
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        String clientIp = getClientIp(request);
        String requestId = java.util.UUID.randomUUID().toString().substring(0, 8);
        
        log.info("[{}] 请求开始处理 - 路径: {}, 客户端IP: {}", requestId, path, clientIp);
        
        // 仅处理管理员后台API请求
        if (!isAdminPath(path)) {
            log.debug("[{}] 非后台请求路径，跳过处理: {}", requestId, path);
            return chain.filter(exchange);
        }
        
        // 登录接口不需要验证
        if (isNoAuthPath(path)) {
            log.info("[{}] 登录请求，无需验证token: {}", requestId, path);
            return chain.filter(exchange);
        }
        
        log.info("[{}] 开始处理后台请求: {}", requestId, path);
        
        // 从请求头中获取AccessToken
        String accessToken = request.getHeaders().getFirst("Authorization");
        log.debug("[{}] 原始Authorization头: {}", requestId, accessToken);
        //
        if (accessToken != null && accessToken.startsWith("Bearer ")) {
            accessToken = accessToken.substring(7);
            log.debug("[{}] 提取到AccessToken: {}", requestId, maskToken(accessToken));
        } else {
            log.debug("[{}] 请求中没有有效的Bearer Token", requestId);
        }
        // 从url中获取accesstoken
        String tokenFromQuery = exchange.getRequest().getQueryParams().getFirst("token");
        if (tokenFromQuery != null && !tokenFromQuery.isEmpty()) {
            accessToken = tokenFromQuery;
            log.info("[{}] 从查询参数获取到token: {}", requestId, tokenFromQuery);
        } else {
            log.debug("[{}] url中没有有效的Bearer Token", requestId);
        }
        // 检查accessToken是否在黑名单中
        log.info("[{}] accessToken: {}", requestId, accessToken);
        if (accessToken != null) {
            String blacklistKey = "token:blacklist:" + accessToken;
            Boolean isBlacklisted = stringRedisTemplate.hasKey(blacklistKey);
            log.debug("[{}] 检查AccessToken是否在黑名单中: {}", requestId, isBlacklisted);
            
            if (isBlacklisted) {
                log.warn("[{}] AccessToken在黑名单中，拒绝访问: {}", requestId, maskToken(accessToken));
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
        } else {
            log.info("[{}] 被跳过了？", requestId);
        }
        
        // 如果AccessToken不存在或无效，尝试从RefreshToken刷新
        boolean needRefresh = false;
        if (accessToken == null) {
            log.info("[{}] AccessToken不存在，尝试使用RefreshToken", requestId);
            needRefresh = true;
        } else {
            try {
                boolean isValid = jwtUtil.validateToken(accessToken);
                log.debug("[{}] AccessToken有效性验证结果: {}", requestId, isValid);
                if (!isValid) {
                    log.info("[{}] AccessToken无效或已过期，尝试使用RefreshToken", requestId);
                    needRefresh = true;
                }
            } catch (Exception e) {
                log.warn("[{}] AccessToken验证时发生异常: {}", requestId, e.getMessage());
                needRefresh = true;
            }
        }
        
        if (needRefresh) {
            log.info("[{}] 开始尝试使用RefreshToken刷新AccessToken", requestId);
            
            // 从Cookie中获取RefreshToken
            HttpCookie refreshTokenCookie = request.getCookies().getFirst("admin_refresh_token");
            log.info("[{}] request.getCookies(): {}",requestId,request.getCookies());
            if (refreshTokenCookie == null) {
                log.warn("[{}] RefreshToken Cookie不存在，拒绝访问", requestId);
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            
            String refreshToken = refreshTokenCookie.getValue();
            log.debug("[{}] 从Cookie中获取到RefreshToken: {}", requestId, maskToken(refreshToken));
            
            // 检查refreshToken是否在黑名单中
            String refreshBlacklistKey = "token:blacklist:" + refreshToken;
            Boolean isRefreshBlacklisted = Boolean.TRUE.equals(stringRedisTemplate.hasKey(refreshBlacklistKey));
            log.debug("[{}] 检查RefreshToken是否在黑名单中: {}", requestId, isRefreshBlacklisted);
            
            if (isRefreshBlacklisted) {
                log.warn("[{}] RefreshToken在黑名单中，拒绝访问", requestId);
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            
            // 验证RefreshToken
            boolean isRefreshValid = false;
            try {
                isRefreshValid = jwtUtil.validateToken(refreshToken);
                log.debug("[{}] RefreshToken有效性验证结果: {}", requestId, isRefreshValid);
            } catch (Exception e) {
                log.warn("[{}] RefreshToken验证时发生异常: {}", requestId, e.getMessage());
            }
            
            if (!isRefreshValid) {
                log.warn("[{}] RefreshToken无效，拒绝访问", requestId);
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            
            try {
                // 解析RefreshToken
                Map<String, Object> claims = jwtUtil.parseToken(refreshToken);
                log.debug("[{}] RefreshToken解析成功，包含claims: {}", requestId, claims.keySet());
                
                // 检查token类型
                String tokenType = claims.get("type") != null ? claims.get("type").toString() : null;
                log.debug("[{}] RefreshToken类型: {}", requestId, tokenType);
                
                if (tokenType == null || !"refresh".equals(tokenType)) {
                    log.warn("[{}] Token类型错误，期望refresh但获得: {}", requestId, tokenType);
                    exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                    return exchange.getResponse().setComplete();
                }
                
                // 生成新的AccessToken
                Long adminId = claims.get("id") != null ? Long.valueOf(claims.get("id").toString()) : null;
                log.info("[{}] claims: {}", requestId, claims);
                String username = claims.get("username").toString() != null ? claims.get("username").toString() : null;
                Integer roleId = (Integer) claims.get("role_id");
                if (adminId == null) {
                    log.warn("[{}] RefreshToken中没有找到管理员ID", requestId);
                    exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                    return exchange.getResponse().setComplete();
                }
                
                log.info("[{}] 为管理员ID: {} 生成新的AccessToken", requestId, adminId);
                
                // 这里简化处理，直接使用RefreshToken中的信息生成新的AccessToken
                Map<String, Object> accessClaims = new HashMap<>();
                accessClaims.put("id", adminId);
                accessClaims.put("username", username);
                accessClaims.put("role_id", roleId);
                accessClaims.put("type", "access");
                
                // 生成新的AccessToken
                accessToken = jwtUtil.genToken(accessClaims);
                log.debug("[{}] 生成新的AccessToken: {}", requestId, maskToken(accessToken));
                
                // 在响应头中返回新的AccessToken
                exchange.getResponse().getHeaders().add("New-Access-Token", accessToken);
                log.info("[{}] 已在响应头中添加新的AccessToken", requestId);
                
            } catch (Exception e) {
                log.error("[{}] 处理RefreshToken时发生错误: {}", requestId, e.getMessage(), e);
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
        }
        
        try {
            // 验证AccessToken
            if (accessToken == null) {
                log.warn("[{}] AccessToken为空，无法验证", requestId);
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            
            Map<String, Object> claims = jwtUtil.parseToken(accessToken);
            log.debug("[{}] AccessToken解析成功，包含claims: {}", requestId, claims.keySet());
            
            // 检查token类型
            String tokenType = claims.get("type") != null ? claims.get("type").toString() : null;
            log.debug("[{}] AccessToken类型: {}", requestId, tokenType);
            
            if (tokenType == null || !"access".equals(tokenType)) {
                log.warn("[{}] Token类型错误，期望access但获得: {}", requestId, tokenType);
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete();
            }
            
            // 将用户信息添加到请求头
            String adminId = claims.get("id") != null ? claims.get("id").toString() : "";
            String adminRole = claims.getOrDefault("roleId", "").toString();
            
            log.info("[{}] 验证通过，将管理员信息添加到请求头 - ID: {}, 角色: {}", requestId, adminId, adminRole);
            
            ServerHttpRequest mutatedRequest = request.mutate()
                .header("uid", adminId)
                .header("admin-role", adminRole)
                .header("request-id", requestId)
                .header("nickName", adminRole + ":" + adminId)
                .header("userType", "admin")
                .build();
            log.info("[{}] 请求处理完成，转发到后续服务", requestId);
            return chain.filter(exchange.mutate().request(mutatedRequest).build());
            
        } catch (Exception e) {
            log.error("[{}] 验证AccessToken时发生错误: {}", requestId, e.getMessage(), e);
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
    }
    
    /**
     * 判断是否是后台管理路径
     * @param path 请求路径
     * @return 是否是后台路径
     */
    private boolean isAdminPath(String path) {
        return ADMIN_PATH_PREFIXES.stream().anyMatch(path::startsWith);
    }
    
    /**
     * 判断是否是无需验证的路径
     * @param path 请求路径
     * @return 是否无需验证
     */
    private boolean isNoAuthPath(String path) {
        return NO_AUTH_PATHS.stream().anyMatch(path::equals);
    }
    
    /**
     * 获取客户端IP地址
     * @param request HTTP请求
     * @return IP地址
     */
    private String getClientIp(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ip = headers.getFirst("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress().getHostString();
        }
        // 对于通过多个代理的情况，第一个IP为客户端真实IP
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }
    
    /**
     * 掩码处理token，只显示前10个和后10个字符
     * @param token 原始token
     * @return 掩码处理后的token
     */
    private String maskToken(String token) {
        if (token == null || token.length() <= 20) {
            return "***masked***";
        }
        return token.substring(0, 10) + "..." + token.substring(token.length() - 10);
    }
    
    @Override
    public int getOrder() {
        return -100; // 确保在其他过滤器之前执行
    }
}
